/*
 * mini-mvvm 解决读写表单的痛点
 * @Author: kevin.huang
 * @Date: 2018-08-21 09:09:53 
 * @Last Modified by: kevin.huang
 * @Last Modified time: 2019-05-07 21:17:09
 * Copyright (c): kevin.huang Released under MIT License
 */
/**
 * 2019-05-15 数据data对象添加一个toJson api destroy内调用clearWatchers清除数据对象上的观察者
 * 2019-05-13 新增对k_window_input支持适用于属性表单的window选择输入input
 * 2019-05-03 支持 bui中的calender时间日期支持
 * **/
(function (global, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['jquery'], function ($) {
            return factory(global, $);
        });
    } else {
        factory(global, $);
    }
}(typeof window !== "undefined" ? window : this, function (window, $) {
    "use strict";
    var noCompileAttrs = {
        "type": true,
        "name": true,
        "watcher": true,
        "id": true
    };
    var $B = window["$B"] ? window["$B"] : {};
    String.prototype.trim = function () {
        return this.replace(/(^\s*)|(\s*$)/g, "");
    };
    String.prototype.leftTrim = function () {
        return this.replace(/(^\s*)/g, "");
    };
    String.prototype.rightTrim = function () {
        return this.replace(/(\s*$)/g, "");
    };
    var bindReg = /\{\s*\{(.*)\}\s*\}/;
    var expressCache = {};
    var clearExpressCacheTimer;

    function getExpressArray(textContent) {
        if (expressCache[textContent]) {
            return expressCache[textContent];
        }
        var res = textContent.match(/\{\{((?!\}\}).)+\}+/g);
        var expressArray = [];
        for (var i = 0, len = res.length; i < len; ++i) {
            var s = res[i].replace(/\s*\{\{\s*/, "").replace(/\s*\}\}\s*/, "");
            expressArray.push(s);
        }
        expressCache[textContent] = expressArray;
        clearTimeout(clearExpressCacheTimer);
        clearExpressCacheTimer = setTimeout(function () {
            expressCache = {};
        }, 1200);
        return expressArray;
    }
    var funcBodyCache = {};
    var clearFuncBodyCacheTimer;

    function getfuncBody(express) {
        var fbody = funcBodyCache[express];
        if (fbody) {
            return fbody;
        }
        fbody = express.replace(/{\s*\{/g, "").replace(/\}\s*\}/g, "").trim();
        funcBodyCache[express] = fbody;
        clearTimeout(clearFuncBodyCacheTimer);
        clearFuncBodyCacheTimer = setTimeout(function () {
            funcBodyCache = {};
        }, 1200);
        return fbody;
    }
    function clearWatchers(obj){
        if(obj){
            var keys = Object.keys(obj);
            var key ,v;
            for (var i = 0, len = keys.length; i < len; ++i) {
                key = keys[i];
                if(key === "_$watchers" ){
                    obj[key] = [];
                }else {
                    v = obj[key];
                    if (typeof v !== "toJson" && typeof v !== "function"  && key !== "_$path") {
                        if ($.isPlainObject(v)) {
                            clearWatchers(v);
                        }
                    }
                }
            }
        }        
    }
    /**
     * 递归对象自身属性
     * ***/
    function loopSelfProps(obj){
        var keys = Object.keys(obj);
        var key, v,json = {};
        for (var i = 0, len = keys.length; i < len; ++i) {
            key = keys[i];
            v = obj[key];
            if (typeof v !== "toJson" && typeof v !== "function" && key !== "_$watchers" && key !== "_$path") {
                if ($.isPlainObject(v)) {
                    json[key] = loopSelfProps(v);
                } else {
                    json[key] = v;
                }
            }
        }
        return json;
    }

    /***
     * 观察者，属性值变更时候，通知观察者更新UI，UI更新时候，通知观察者更新属性
     * 一个绑定属性一个wathcer，一个wathcer里面可能有多个绑定的data字段属性,
     * wather可能同时监听着不同的数据节点
     * 从expressArray中解析出数据节点，及对应的属性名称，记录到wather中
     * ***/
    function Watcher(vm, node, replaceContent, attrName) {
        this.vm = vm;
        this.attrName = attrName ? attrName.toLowerCase() : attrName;
        this.el = node;
        this.$el = $(this.el);
        this.nodeName = node.nodeName;
        this.replaceContent = replaceContent;
        var expressArray = getExpressArray(replaceContent);
        this.methodKeys = [];
        this.propPathArray = [];
        this.propObjectArray = [];
        var express;
        var mapHasset = {}; //防止重复解析 注册
        var i, len;
        //解析出属性对象，将watcher注册到属性对象上
        for (i = 0, len = expressArray.length; i < len; ++i) {
            express = expressArray[i];
            express = getfuncBody(express);
            this.methodKeys.push(express);
            var expArray = express.match(/this\.data\.[^\s*]+/g);
            for (var j = 0, jlen = expArray.length; j < jlen; ++j) {
                express = expArray[j];
                if (!mapHasset[express]) {
                    mapHasset[express] = true;
                    var extractArray = this.vm.extractPropObject(express);
                    for (var k = 0, klen = extractArray.length; k < klen; k++) {
                        var extract = extractArray[k];
                        var propObject = extract.propObject;
                        this.propPathArray.push(extract.propPath);
                        //属性对象上注册本观察者
                        if (!propObject._$watchers) {
                            propObject._$watchers = [];
                        }
                        propObject._$watchers.push(this);
                        this.propObjectArray.push(propObject);
                    }
                }
            }
        }
        var _this = this;
        if (this.attrName === "express") { //自定义解析，自定义处理watcher
            var watcher = this.$el.attr("watcher");
            if (watcher && typeof this.vm[watcher] === "function") {
                this.vm[watcher](this);
            }
        } else {
            var isBindUserInput = this.el.nodeType === 1;
            var isTextArea = this.el.parentNode.nodeName === "TEXTAREA"; //对TEXTAREA进行用户输入绑定
            if (isTextArea) {
                isBindUserInput = true;
            }
            //绑定UI用户操作--->data的联动            
            if (isBindUserInput) {
                var updateTimer = 100;
                //var elType = this.$el.attr("type").toLowerCase();
                //console.log(" elType == " + elType + " this.attrName = " + this.attrName);
                this.userInputTimer;
                var eventName, eventEl = this.$el;
                if (this.attrName === "value" || isTextArea) {
                    eventName = "input";
                    if (isTextArea) {
                        eventEl = $(this.el.parentNode);
                    }
                } else if (this.attrName === "checked" || this.attrName === "selected") { //radio、checkbox、select联动
                    updateTimer = 0;
                    eventName = "change";
                    if (this.nodeName === "OPTION" && this.attrName === "selected") { //下拉选框
                        eventEl = this.$el.parent();
                        if (eventEl.data("hasBindWatcher")) {
                            eventName = undefined;
                        } else {
                            eventEl.data("hasBindWatcher", true);
                        }
                    }
                }
                if (eventName) {
                    eventEl.on(eventName + ".mvvm", function () {
                        if (_this.propChangeUpdating) { //避免循环联动
                            //console.log("避免循环联动 >>>>>>> propChangeUpdating");
                            return false;
                        }                        
                        var el = this;
                        var $t = $(this);
                        var txt = $t.val();
                        var eleType = $t.attr("type");
                        if(eleType){
                            eleType = eleType.toLowerCase();
                        }
                        clearTimeout(_this.userInputTimer);
                        if (eleType === "radio") { //
                            var name = $t.attr("name");
                            var allRadio = _this.vm.$form.find("input[name=" + name + "]");
                            allRadio.each(function () {
                                var $r = $(this);
                                if (this !== el) {
                                    $r.removeAttr("checked");
                                } else {
                                    $r.attr("checked", "checked").prop("checked", true);
                                }
                            });
                            //no2update不需要再对统一组name的radio属性set监听更新
                            allRadio.data("no2update", true);
                            setTimeout(function () {
                                allRadio.removeData("no2update");
                            }, 100);
                        } else if ($t[0].nodeName === "SELECT") {
                            //no2update不需要对同一组的option进行属性set监听更新
                            var opts = $t.children().data("no2update", true);
                            opts.each(function () {
                                var opt = $(this);
                                if (txt === opt.val()) {
                                    opt.prop("selected", true).attr("selected", "selected");
                                } else {
                                    opt.prop("selected", false).removeAttr("selected");
                                }
                            });
                            setTimeout(function () {
                                opts.removeData("no2update");
                            }, 100);
                        }
                        _this.userInputTimer = setTimeout(function () {                           
                            var paths = _this.propPathArray[0].split(".");
                            var pName = paths[paths.length - 1];
                            var propObject = _this.propObjectArray[0];
                            var oldValue = propObject[pName];
                            if (!$.isPlainObject(oldValue)) {
                                if (typeof oldValue === "number" && txt !== "") {
                                    if (oldValue % 1 === 0) {
                                        txt = parseInt(txt);
                                    } else {
                                        txt = parseFloat(txt);
                                    }
                                    propObject[pName] = txt;
                                } else {
                                    propObject[pName] = txt;
                                }
                            }
                        }, updateTimer);
                    });
                }
            }
        }
    }
    Watcher.prototype = {
        update: function (target, propName, newValue, oldValue) {
            if (this.$el.data("no2update")) {
                return;
            }
            this.propChangeUpdating = true; //避免循环联动
            var path = target._$path ? target._$path + "." + propName : propName;
            if (this.need2Update(path) && newValue !== oldValue) {
                if (this.attrName === "express") { //自定义解析
                    this.expressUpater();
                } else if (this.el.nodeType === 3) { //文本节点更新
                    this.textUpdater();
                } else if (this.nodeName === "OPTION" && this.attrName.toLowerCase() === "selected") { //下拉项目更新器
                    this.selectUpdater();
                } else if (this.nodeName === "INPUT" && this.$el.attr("type") === "radio") {
                    this.radioUpdater();
                } else { //其他节点更新
                    this.elUpdater();
                }
                var _this = this;
                setTimeout(function () {
                    _this.propChangeUpdating = false;
                }, 20);
            } else {
                this.propChangeUpdating = false;
            }
        },
        need2Update: function (path) {
            var res = false;
            for (var i = 0, len = this.propPathArray.length; i < len; ++i) {
                var p = this.propPathArray[i];
                if (p === path) {
                    res = true;
                    break;
                } else if (p.indexOf(path + ".") === 0) { //子节点替换
                    res = true;
                    break;
                }
            }
            return res;
        },
        /**express自定义更新解析**/
        expressUpater: function () {
            var mKey;
            for (var i = 0, len = this.methodKeys.length; i < len; ++i) {
                mKey = this.methodKeys[i];
                this.vm.callExpressFuntion(mKey, this.$el);
            }
        },
        //radio更新器
        radioUpdater: function () {
            var _this = this;
            _this._invokeMethods(function (mKey, value) {
                if (value) {
                    _this.$el.attr("checked", "checked").prop("checked", true);
                } else {
                    _this.$el.removeAttr("checked").prop("checked", false);
                }
            });
        },
        /**下拉更新项**/
        selectUpdater: function () {
            var _this = this;
            _this._invokeMethods(function (mKey, value) {
                if (value) {
                    _this.$el.prop("selected", true).attr("selected", "selected");
                } else {
                    _this.$el.prop("selected", false).removeAttr("selected");
                }
            });
        },
        /**文本节点更新器**/
        textUpdater: function () {
            var text = this.replaceContent,
                need2Update = false;
            this._invokeMethods(function (mKey, value) {
                if (typeof value !== "undefined") {
                    mKey = mKey.replace(/\+/g, "\\+").replace(/\-/g, "\\-").replace(/\*/g, "\\*");
                    var exp = "{\\s*{\\s*" + mKey + "\\s*}\\s*}";
                    var reg = new RegExp(exp, "g");
                    text = text.replace(reg, value);
                    need2Update = true;
                }
            });
            if (need2Update) {
                this.el.textContent = text;
                this.el.nodeValue = text;
                this.el.data = text;
                if(this.el.parentNode.nodeName === "TEXTAREA"){//兼容TEXTAREA
                    this.el.parentNode.value = text;
                    this.el = this.el.parentNode.firstChild;
                }
            }
        },
        /**元素属性更新器**/
        elUpdater: function () {
            var _this = this;
            this._invokeMethods(function (mKey, value) {
                _this.$el.attr(_this.attrName, value);
                if (_this.attrName === "value") {
                    _this.$el.val(value);
                }
            });
        },
        /***调用所有监听的function***/
        _invokeMethods: function (callFn) {
            var mKey, value;
            for (var i = 0, len = this.methodKeys.length; i < len; ++i) {
                mKey = this.methodKeys[i];
                value = this.vm.callExpressFuntion(mKey);
                callFn(mKey, value);
            }
        },
        destroy: function () {
            for (var p in this) {
                if (this.hasOwnProperty(p)) {                   
                    delete this[p];
                }
            }
        }
    };
    /***
     * 定义html模板编译解析器
     * 编译解析模板，将模板绑定与data属性进行双向关联
     * ***/
    function Compiler(vm) {
        this.vm = vm;
        this.el = vm.el;
        this.$el = vm.$form;
        this.data = vm.data;
        this.watchers = [];
        this.compile(this.el);
    }
    Compiler.prototype = {
        compile: function (el) {
            this.compileingEl = el;
            if (el.nodeType === 3) {
                this.compileTextNode(el);
            } else if (el.nodeType === 1) {
                this.compileElement(el);
            }
        },
        /**解析节点元素
         * 解析元素的属性
         * **/
        compileElement: function (el) {
            var nodeName = el.nodeName;
            if (nodeName === "SCRIPT" || nodeName === "STYLE") {
                return;
            }
            var attributes = el.attributes;
            var me = this;
            var $el = $(el);
            var attr, i, len;
            //编译元素属性 
            for (i = 0, len = attributes.length; i < len; ++i) {
                attr = attributes[i];
                if(!attr){
                    continue;
                }
                var attrName = attr.name;
                if (!noCompileAttrs[attrName]) {
                    var expValue = attr.value;
                    var value;
                    if (bindReg.test(expValue)) {
                        expValue = expValue.replace(/\{\s*\{/g, "{{");
                        expValue = expValue.replace(/\}\s*\}/g, "}}");
                        //console.log("compileElement >>>>>>>>>>>>>>>>>>>>> attrName = " + attrName + expValue);
                        if (nodeName === "INPUT" && attrName === "value") {
                            value = me.invokeExpressMethod(expValue);
                            $el.attr("value", value);
                            //创建一个节点Watcher，将Watcher注册到对应的属性节点节点上
                            me.watchers.push(new Watcher(me.vm, el, expValue, attrName));
                        } else if (nodeName === "INPUT" && attrName === "checked") { //表达式属性                        
                            value = me.invokeExpressMethod(expValue);
                            $el.removeAttr("checked");
                            if (value) {
                                $el.attr("checked", "checked");
                                $el.prop("checked", true);
                            }
                            me.watchers.push(new Watcher(me.vm, el, expValue, attrName));
                        } else if (nodeName === "OPTION" && attrName === "selected") { //表达式属性
                            value = me.invokeExpressMethod(expValue);
                            $el.removeAttr("selected");
                            if (value) {
                                $el.attr("selected", "selected");
                                $el.prop("selected", true);
                            }
                            me.watchers.push(new Watcher(me.vm, el, expValue, attrName));
                        } else if (attrName === "express") { //扩展解析
                            var extFunctionBody = getfuncBody(expValue);
                            var extFunction = me.makeExpressionFunction(extFunctionBody);
                            extFunction.call(me.vm, $el);
                            me.watchers.push(new Watcher(me.vm, el, expValue, attrName));
                        }
                        // else if (attrName === "style") {
                        //     //console.log("待完成 ");
                        // } else if (attrName === "class") {
                        //     //console.log("待完成 ");
                        // } 
                        else { //其他属性
                            value = me.invokeExpressMethod(expValue);
                            $el.attr(attr.name, value);
                            me.watchers.push(new Watcher(me.vm, el, expValue, attrName));
                        }
                    }
                }
            }
            // });
            //递归子元素           
            var childNodes = el.childNodes;
            var child;
            for (i = 0, len = childNodes.length; i < len; ++i) {
                child = childNodes[i];
                this.compile(child);
            }
        },
        /**解析文本节点**/
        compileTextNode: function (node) {
            var textContent = node.textContent;
            if (bindReg.test(textContent)) {
                textContent = textContent.replace(/\{\s*\{/g, "{{");
                textContent = textContent.replace(/\}\s*\}/g, "}}");
                var value = this.invokeExpressMethod(textContent);
                if (typeof value !== "undefined") {
                    node.textContent = value;
                    //创建一个节点Watcher，将Watcher注册到对应的属性节点节点上
                    this.watchers.push(new Watcher(this.vm, node, textContent));
                }
            }
        },
        getExpressMethod: function (textContent) {
            return this.invokeExpressMethod(textContent, true);
        },
        invokeExpressMethod: function (textContent, isGetMethod) {
            var res = getExpressArray(textContent);
            if (res) {
                var returnValue = textContent,
                    express, funcBody, expFn, tmpVal, reg;
                for (var j = 0, len1 = res.length; j < len1; ++j) {
                    express = res[j];
                    funcBody = getfuncBody(express);
                    expFn = this.vm.expressMethodCache[funcBody];
                    if (!expFn) {
                        expFn = this.makeExpressionFunction(funcBody);
                    }
                    if (isGetMethod) {
                        return expFn;
                    }
                    tmpVal = expFn.call(this.vm);
                    if (typeof tmpVal === "boolean") {
                        return tmpVal;
                    }
                    express = express.replace(/\+/g, "\\+").replace(/\-/g, "\\-").replace(/\*/g, "\\*");
                    express = express.replace(/\(/g, "\\(").replace(/\)/g, "\\)");
                    express = express.replace(/\{/g, "\\{").replace(/\}/g, "\\}");
                    reg = new RegExp(express, "g");
                    returnValue = returnValue.replace(reg, tmpVal);
                }
                returnValue = returnValue.replace(/\{\{/g, "").replace(/\}\}/g, "");
                return returnValue;
            }
        },
        makeExpressionFunction: function (expression) {
            var fn = this.vm.expressMethodCache[expression];
            if (!fn) {
                var funcBody;
                if (expression.indexOf("return") > 0) {
                    funcBody = expression;
                } else {
                    funcBody = 'return ' + expression + ';';
                }
                fn = new Function("el", funcBody);
                this.vm.expressMethodCache[expression] = fn;
            }
            return fn;
        },
        destroy: function () {
            for(var i = 0 ,len = this.watchers.length ; i < len ;++i){
                this.watchers[i].destroy();
            }
            for (var p in this) {
                if (this.hasOwnProperty(p)) {                   
                    delete this[p];
                }
            }
        }
    };
    /*****
     * 属性包装
     * 利用Object.defineProperty对data进行同名属性覆盖定义
     * 利用闭包作用域确保重新定义的get/set是对原生data属性值的封装
     * 利用get/set观察赋值、取值
     * ******/
    function Observer(dataObj, vm) {
        this.data = dataObj;
        this.vm = vm;
        var me = this,
            paths,
            key,
            value;
        var keys = Object.keys(dataObj);
        for (var i = 0, len = keys.length; i < len; ++i) {
            key = keys[i];
            if (key !== "_$watchers" && key !== "_$path" && key !== "toJson") {
                paths = [];
                value = dataObj[key];
                if ($.isPlainObject(value)) {
                    paths.push(key);
                }
                me.getSetBuild(dataObj, key, value, paths);
            }
        }
    }
    Observer.prototype = {
        /**递归属性**/
        forProps: function (dataObj, paths) {
            if (!dataObj || !$.isPlainObject(dataObj)) {
                return;
            }
            dataObj["_$path"] = paths.join(".");
            var me = this,
                key, val, pathCopy;
            var keys = Object.keys(dataObj);
            for (var i = 0, len = keys.length; i < len; ++i) {
                key = keys[i];
                if (key !== "_$watchers" && key !== "_$path" && key !== "toJson") {
                    val = dataObj[key];
                    pathCopy = paths.slice();
                    if ($.isPlainObject(val)) {
                        pathCopy.push(key);
                    }
                    me.getSetBuild(dataObj, key, val, pathCopy);
                }
            }
        },
        /**get/set重新定义包装**/
        getSetBuild: function (data, key, value, paths) {
            var me = this;
            this.forProps(value, paths); //递归包装
            //修复
            Object.defineProperty(data, key, {
                enumerable: true,
                configurable: true,
                set: function (newValue) {
                    if ((value + "") === (newValue + "")) {
                        return;
                    }
                    var oldValue = value;
                    //拷贝watcher到新的对象值上
                    if ($.isPlainObject(value) && $.isPlainObject(newValue)) {
                        me.copyWatcher(value, newValue);
                    }
                    value = newValue;
                    me.forProps(value, paths); //递归包装新值
                    me.onSet(this, key, newValue, oldValue);
                },
                get: function () {
                    return value;
                }
            });
        },
        /**
         * 递归
         * 拷贝watcher到新的对象值上 
         * **/
        copyWatcher: function (oldObject, newObject) {
            if (oldObject["_$watchers"]) {
                newObject["_$watchers"] = oldObject["_$watchers"];
                oldObject["_$watchers"] = undefined;
            }
            if (oldObject["_$path"]) {
                newObject["_$path"] = oldObject["_$path"];
                oldObject["_$path"] = undefined;
            }
            var me = this,
                oldObj, prop;
            var props = Object.keys(oldObject);
            for (var i = 0, len = props.length; i < len; ++i) {
                prop = props[i];
                oldObj = oldObject[prop];
                if (typeof oldObj === "object") {
                    var newObj = newObject[prop];
                    if (typeof newObj === "object") {
                        me.copyWatcher(oldObj, newObj);
                    }
                }
            }
        },
        onSet: function (target, propName, newValue, oldValue) {
            if (target["_$watchers"]) { //是否存在观察者！！
                for (var i = 0, len = target["_$watchers"].length; i < len; ++i) {
                    var watcher = target["_$watchers"][i];
                    watcher.update(target, propName, newValue, oldValue);
                }
            }
            var _this = this;
            if (typeof this.vm.onChanged === "function") {
                _this.vm.onChanged.call(_this.vm, target, propName, newValue, oldValue);
                // clearTimeout(_this.onChangedTimer);
                // _this.onChangedTimer = setTimeout(function () {
                //     _this.vm.onChanged.call(_this.vm, target, propName, newValue, oldValue);
                // }, 300);
            }
        },
        destroy: function () {
            for (var p in this) {
                if (this.hasOwnProperty(p)) {                   
                    delete this[p];
                }
            }
        }
    };
    /**
     * mvvm定义
     * **/
    function Mvm(options) {
        this.el = typeof options.el === "string" ? document.getElementById(options.el) : options.el;
        this.$form = $(this.el);
        this.options = options;
        this.data = options.data;
        this.onChanged = options.onChanged;
        this.expressMethodCache = {}; //表达式动态function缓存
        this.pathObjectsCache = {};
        this.initing = true;
        //处理自定义的comboxWatcher 、 comboxExpress
        this._attachDiyRegiste(this.options.registeWatchers);
        this._attachDiyRegiste(this.options.registeExpress);
        this.observer = new Observer(this.data, this); //先对属性进行get/set包装 
        this.compiler = new Compiler(this); //编译解析模板，将模板绑定与data属性进行双向关联
        var _this = this;
        setTimeout(function(){
            _this.initing = true;
        },50);
        //补偿一个toJson API
        if(typeof this.data.toJson !== "function"){
            this.data.toJson = function(){                
                var json = {};
                var v, key;
                var keys = Object.keys(this);
                for (var i = 0, len = keys.length; i < len; ++i) {
                    key = keys[i];
                    v = this[key];
                    if ( typeof v !== "function" && key !== "toJson" && key !== "_$watchers" && key !== "_$path") {
                        if ($.isPlainObject(v)) {
                            json[key] = loopSelfProps(v);
                        } else {
                            json[key] = v;
                        }
                    }
                }
                //console.log("data.toJson >>>>");
                return json;
            };
        }
    }
    Mvm.prototype = {
        destroy: function () {
           // console.log("destroy >>>  mvvm obj");
            //解除数据对象上的_$watchers
            clearWatchers(this.data);
            this.observer.destroy();
            this.compiler.destroy();
            for (var p in this) {
                if (this.hasOwnProperty(p)) {                   
                    delete this[p];
                }
            }
        },
        /***
         * 根据路径抽取属性对象,采用缓存避免重复解析提取
         * path 可能是个逻辑表达式 this.data.form.sex === 1 ? true : false
         * 也可能是 this.data.form.userName
         * propObject:属性对象，propNames属性名称集合
         * ***/
        extractPropObject: function (path) {
            var cacheKey = path;
            var retArray = this.pathObjectsCache[cacheKey];
            if (retArray) {
                return retArray;
            }
            var res = path.match(/this\.data\.[^\s+|^,]+/g);
            retArray = [];
            var rootObject = this.data,
                tmpObj,
                tmpArray;
            for (var j = 0, jlen = res.length; j < jlen; ++j) {
                path = res[j].replace("this.data.", "");
                tmpObj = rootObject;
                if (path.indexOf(".") > 0) {
                    tmpArray = path.split(".");
                    for (var i = 0, len = tmpArray.length - 1; i < len; ++i) {
                        if (i < len) {
                            tmpObj = tmpObj[tmpArray[i]];
                        }
                    }
                }
                retArray.push({
                    propObject: tmpObj,
                    propPath: path
                });
            }
            this.pathObjectsCache[cacheKey] = retArray;
            return retArray;
        },
        //附加自定义的comboxWatcher 、 comboxExpress
        _attachDiyRegiste: function (regiter) {
            var keys, i, len, fn, key;
            if ($.isPlainObject(regiter)) {
                keys = Object.keys(regiter);
                for (i = 0, len = keys.length; i < len; ++i) {
                    key = keys[i];
                    fn = regiter[key];
                    if (typeof this[key] === "function") {
                        console.log("key[" + key + "]已经存在！");
                        continue;
                    }
                    if (typeof fn === "function") {
                        this[key] = fn;
                    }
                }
            }
        },
        /**
         * 注册监听器
         * **/
        registeWatcher: function (watcherName, wathcerFn) {
            if (typeof this[watcherName] === "function") {
                console.log("watcher[" + watcherName + "]已经存在！");
            }
            this[watcherName] = wathcerFn;
            return this;
        },
        /**
         * 注册表达式处理解析器
         * **/
        registeExpress: function (expressName, expressFn) {
            if (typeof this[expressName] === "function") {
                console.log("express[" + expressName + "]已经存在！");
            }
            this[expressName] = expressFn;
            return this;
        },
        /**
         * 编译某个元素
         * **/
        compile: function (el) {
            if (el.css) {
                el = el[0];
            }
            this.compiler.compile(el);
            return this;
        },
        /***
         * 调用表达式对应的function
         * ***/
        callExpressFuntion: function (express, args) {
            var m = this.expressMethodCache[express];
            if (m) {
                return m.call(this, args);
            }
            return undefined;
        },
        _forProps: function (obj, json) {
            var _this = this,
                v, key;
            var keys = Object.keys(obj);
            for (var i = 0, len = keys.length; i < len; ++i) {
                key = keys[i];
                v = obj[key];
                if (typeof v !== "function" && key !== "_$watchers" && key !== "_$path") {
                    if ($.isPlainObject(v)) {
                        json[key] = {};
                        _this._forProps(v, json[key]);
                    } else {
                        json[key] = v;
                    }
                }
            }
        },
        getJson: function () {
            var json = {};
            var _this = this,
                v, key;
            var keys = Object.keys(this.data);
            for (var i = 0, len = keys.length; i < len; ++i) {
                key = keys[i];
                v = _this.data[key];
                if (typeof v !== "toJson" && typeof v !== "function" && key !== "_$watchers" && key !== "_$path") {
                    if ($.isPlainObject(v)) {
                        json[key] = {};
                        _this._forProps(v, json[key]);
                    } else {
                        json[key] = v;
                    }
                }
            }
            if(this.options.onGetJson){
                this.options.onGetJson(json);
            }
            return json;
        },
        /**
         * 复选框监听器
         * **/
        checkboxWather: function (wather) {
            var propObject = wather.propObjectArray[0];
            var propPaths = wather.propPathArray[0].split(".");
            var propName = propPaths[propPaths.length - 1];
            wather.$el.on("click", function () {
                var $t = $(this);
                var val = $t.val();
                var propVals = propObject[propName];
                var isNumeric = false;
                var isArray = $.isArray(propVals);
                var isDotSplit = false;
                if (isArray) {
                    if (propVals.length > 0) {
                        isNumeric = $.isNumeric(propVals[0]);
                    }
                } else {
                    isDotSplit = propVals.indexOf(";") > 0;
                    isNumeric = false;
                    if (propVals === "") {
                        propVals = [];
                    } else {
                        if (isDotSplit) {
                            propVals = propVals.split(";");
                        } else {
                            propVals = propVals.split(",");
                        }
                    }
                }
                if (isNumeric) {
                    val = parseInt(val);
                }
                var newValues = propVals;
                if ($t.prop("checked")) {
                    $t.attr("checked", "checked");
                    propVals.push(val);
                } else {
                    $t.removeAttr("checked");
                    newValues = [];
                    for (var i = 0, len = propVals.length; i < len; ++i) {
                        if (propVals[i] !== "" && propVals[i] !== val) {
                            newValues.push(propVals[i]);
                        }
                    }
                }
                if (!isArray) {
                    if (isDotSplit) {
                        propObject[propName] = newValues.join(";");
                    } else {
                        propObject[propName] = newValues.join(",");
                    }
                }
            });
        },
        /**
         *复选框特有的解析 
         **/
        checkboxExpress: function (propVal, el) {
            var v = el.val();
            if (!$.isArray(propVal)) {
                if (propVal.indexOf(",") > 0) {
                    propVal = propVal.split(",");
                } else if (propVal.indexOf(";") > 0) {
                    propVal = propVal.split(";");
                }
            }
            var isChecked = false;
            for (var i = 0, len = propVal.length; i < len; ++i) {
                if (v === (propVal[i] + "")) {
                    isChecked = true;
                    break;
                }
            }
            if (isChecked) {
                el.prop("checked", true).attr("checked", "checked");
            } else {
                el.prop("checked", false).removeAttr("checked");
            }
        },
        kRadioWatcher: function (watcher) {
            var $el = watcher.$el;
            var propObject = watcher.propObjectArray[0];
            var propName = watcher.propPathArray[0];
            $el.children("input").on("change", function () {
                var v = $(this).val();
                var oldValue = propObject[propName];
                var isNumeric = $.isNumeric(oldValue);
                if (isNumeric) {
                    propObject[propName] = parseInt(v);
                } else {
                    propObject[propName] = v;
                }
            });
        },
        kRadioExpress: function (data, el) {
            var $el = $(el);
            $el.find("input[type=radio]").each(function () {
                var box = $(this);
                var v = box.val();
                if (v === (data + "")) {
                    box.attr("checked", "checked").prop("checked", true);
                } else {
                    box.removeAttr("checked").prop("checked", false);
                }
            });
        },
        kcheckBoxWatcher: function (watcher) {
            var $el = watcher.$el;
            var propObject = watcher.propObjectArray[0];
            var propName = watcher.propPathArray[0];
            $el.children("input").on("change", function () {
                var $input = $(this);
                var propVal = propObject[propName];
                var valArray = [];
                var joinChart;
                if (propVal !== "") {
                    if (propVal.indexOf(";") > 0) {
                        valArray = propVal.split(";");
                        joinChart = ";";
                    } else {
                        valArray = propVal.split(",");
                        joinChart = ",";
                    }
                }
                var v = $input.val();
                if ($input.prop("checked")) {
                    valArray.push(v);
                } else {
                    var newArray = [];
                    for (var i = 0, len = valArray.length; i < len; ++i) {
                        if (v !== valArray[i]) {
                            newArray.push(valArray[i]);
                        }
                    }
                    valArray = newArray;
                }
                propObject[propName] = valArray.join(joinChart);
            });
        },
        kcheckBoxExpress: function (data, el) {
            var $el = $(el);
            $el.find("input[type=checkbox]").each(function () {
                var box = $(this);
                var v = box.val();
                var isChecked = false;
                var patt1;
                if (data.indexOf(";") >= 0) {
                    patt1 = new RegExp(";" + v + ";|;" + v + "|" + v + ";");
                    isChecked = patt1.test(data);
                } else if (data.indexOf(",") >= 0) {
                    patt1 = new RegExp("," + v + ",|," + v + "|" + v + ",");
                    isChecked = patt1.test(data);
                } else {
                    isChecked = data === v;
                }
                if (isChecked) {
                    box.attr("checked", "checked").prop("checked", true);
                } else {
                    box.removeAttr("checked").prop("checked", false);
                }
            });
        },
        /**
         * combox观察者解析
         * **/
        kcomboxWatcher: function (wather) {
            var combox = wather.$el.data("combox");
            var propObject = wather.propObjectArray[0];
            var propPath = wather.propPathArray[0];
            var propValue, userSelIds, ids, i, len;
            if (combox.opts.checkbox) { //多选
                ids = combox.getCheckedIds();
                userSelIds = [];
                for (i = 0, len = ids.length; i < len; ++i) {
                    if(ids[i].id !== ""){
                        userSelIds.push(ids[i].id);
                    }                    
                }
                propValue = userSelIds.join(",");
            } else { //单选
                propValue = combox.jqObj.data("id");
            }
            //通过wather更新userSeletions属性
            if((propObject[propPath] + "") !== propValue){
                wather.$el.data("no2update",true);
                propObject[propPath] = propValue;
                wather.$el.removeData("no2update");
            }            
            combox.regiterFn("onWatcher", function () {
                if (this.opts.checkbox) { //多选
                    var ids = this.getCheckedIds();
                    userSelIds = [];
                    for (i = 0, len = ids.length; i < len; ++i) {
                        if(ids[i].id !== ""){
                            userSelIds.push(ids[i].id);
                        }
                    }
                    propValue = userSelIds.join(",");
                } else {
                    propValue = this.jqObj.data("id");
                }
                if((propObject[propPath] + "") !== propValue){
                    wather.$el.data("no2update",true);
                    propObject[propPath] = propValue;
                    wather.$el.removeData("no2update");
                }
            });
        },
        kcomboxExpress: function (data, $el) { //通过数据反向修改combox，这里不做处理           
            var combox = $el.data("combox");
            combox.setCheckDatas(data);
        },
        /***
         * 时间日期控件解析支持
         * ***/
        kcalendarWatcher:function(wather){
            var calender = wather.$el.data("calender");
            var propObject = wather.propObjectArray[0];
            var propPath = wather.propPathArray[0];
            calender.regiterFn("onWatcher", function () { 
                wather.$el.data("no2update",true);               
                propObject[propPath] = this.target.val();
                wather.$el.removeData("no2update");
            });
            var v = wather.$el.val();
            if(v !== "" && v !==  propObject[propPath]){
                wather.$el.data("no2update",true);
                propObject[propPath] = v;
                wather.$el.removeData("no2update");
            }
        },
        kcalendarExpress:function(data, el){
            if(data !== ""){
                var $el = $(el);
                var calender = $el.data("calender");
                calender.setValue(data);
            }       
        },
        /**
         * 树控件联动支持 k_tree_ul
         * ***/
        ktreeExpress:function(data, el){
            var $el = $(el);
            var treeIns = $el.data("treeIns");
            treeIns.setCheckDatas(data); 
        },  
        ktreeWatcher:function(wather){
            var treeIns = wather.$el.data("treeIns");
            var propObject = wather.propObjectArray[0];
            var propPath = wather.propPathArray[0];
            var propValue = propObject[propPath];
            treeIns.regiterFn("onWatcher", function () {
                var idArr = this.getCheckedData({
                    onlyId: true,
                });
                var tmp = [];
                for(var i = 0 ,len = idArr.length ; i < len ;++i){
                    tmp.push(idArr[i].id);
                }
                propValue = tmp.join(",");
                if((propObject[propPath] + "") !== propValue){
                    wather.$el.data("no2update",true);
                    propObject[propPath] = propValue;
                    wather.$el.removeData("no2update");
                }
            });
          
        },
        /**
         * window弹窗选择 input的解析 
         * **/
        kwindowInputExpress:function(data, el){          
            if(el.attr("forinput") || data.indexOf(",") < 0){
                if(data.indexOf("___") > 0){
                    var valArray = data.split("___");
                    el.val(valArray[1]).attr("_val",valArray[0]);
                }else{
                    el.val(data).attr("_val",data);
                }              
            }else{
                var arr = data.split(",");
                var tmp,txtArr = [];
                for(var i = 0 ,len = arr.length ; i < len ; ++i){
                    tmp = arr[i].split("___");
                    txtArr.push(tmp[1]);
                }
                el.val(txtArr.join(",")).attr("_val",data);
            } 
        },
        kwindowInputWatcher:function(wather){
            var el = wather.$el;
            var propObject = wather.propObjectArray[0];
            var propPath = wather.propPathArray[0];
            //var propValue = propObject[propPath];
            el.on("change",function(){
                console.log("wather input change");
                var $t = $(this);
                var val = $t.attr("_val");
                if($t.attr("forinput") || val.indexOf(",") < 0){
                    wather.$el.data("no2update",true);
                    propObject[propPath] = $t.val();
                    wather.$el.removeData("no2update");
                }else{                    
                    var arr = val.split(",");
                    var tmp,txtArr = [];
                    for(var i = 0 ,len = arr.length ; i < len ; ++i){
                        tmp = arr[i].split("___");
                        txtArr.push(tmp[1]);
                    }
                    $t.val(txtArr.join(","));
                    wather.$el.data("no2update",true);
                    propObject[propPath] = val;
                    wather.$el.removeData("no2update");
                }
            });
        }   
    };
    $B.Mvvm = Mvm;
    window["$B"] = $B;
    return Mvm;
}));